/**
 * 
 */
package cz.cvut.fit.crhonjar.mi.run.hawera.tree.expression;

import cz.cvut.fit.crhonjar.mi.run.hawera.exception.HaweraException;
import cz.cvut.fit.crhonjar.mi.run.hawera.tree.TreeVisitor;

/**
 * @author Jarec
 * 
 */
public class UnaryTree extends ExpressionTree {

	private ExpressionTree expression;
	private UnaryOperator operator;

	/**
	 * constructs unary tree
	 * 
	 * @param expression
	 * @param operator
	 */
	public UnaryTree(ExpressionTree expression, UnaryOperator operator) {
		super();
		this.log.debug("> UnaryTree operator: {}", operator);

		if (expression == null) {
			final String message = "Cannot construct unary tree with null expression!";
			this.log.error(message);
			throw new NullPointerException(message);
		}

		if (operator == null) {
			final String message = "Cannot construct unary tree with null unary operator!";
			this.log.error(message);
			throw new NullPointerException(message);
		}

		this.expression = expression;
		this.operator = operator;

		this.log.debug("< UnaryTree");
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cz.cvut.fit.crhonjar.mi.run.hawera.tree.TreeVisitorAcceptor#accept(cz
	 * .cvut.fit.crhonjar.mi.run.hawera.tree.TreeVisitor)
	 */
	@Override
	public void accept(TreeVisitor visitor) {
		visitor.visitUnary(this);

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see cz.cvut.fit.crhonjar.mi.run.hawera.tree.expression.ExpressionTree#
	 * evaluableAtCompileTime()
	 */
	@Override
	public boolean evaluableAtCompileTime() throws HaweraException {
		this.log.debug("> evaluableAtCompileTime");
		if (this.getExpression().evaluableAtCompileTime()) {
			if ((this.getOperator() == UnaryOperator.NOT)
					&& (this.getExpression().getType() == ExpressionType.BOOLEAN)) {
				this.log.debug("< evaluableAtCompileTime result: true");
				return true;
			} else if (((this.getOperator() == UnaryOperator.MINUS) || (this
					.getOperator() == UnaryOperator.PLUS))
					&& ((this.getExpression().getType() == ExpressionType.FIXED) || (this
							.getExpression().getType() == ExpressionType.FLOAT))) {
				this.log.debug("< evaluableAtCompileTime result: true");
				return true;
			}
		}
		this.log.debug("> evaluableAtCompileTime result: false");
		return false;
	}

	/**
	 * @return the expression
	 */
	public ExpressionTree getExpression() {
		this.log.debug(">< getExpression");
		return this.expression;
	}

	/**
	 * @return the operator
	 */
	public UnaryOperator getOperator() {
		this.log.debug(">< getOperator");
		return this.operator;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * cz.cvut.fit.crhonjar.mi.run.hawera.tree.expression.ExpressionTree#getType
	 * ()
	 */
	@Override
	public ExpressionType getType() throws HaweraException {
		this.log.debug("> getType");
		final ExpressionType type = this.getExpression().getType();
		switch (this.getOperator()) {
		case MINUS:
		case PLUS:
			if ((type == ExpressionType.FIXED)
					|| (type == ExpressionType.FLOAT)) {
				this.log.debug("< getType result: {}", type);
				return type;
			} else {
				this.log.debug("< getType result: {}", ExpressionType.OBJECT);
				return ExpressionType.OBJECT;
			}
		case NOT:
			if (type == ExpressionType.BOOLEAN) {
				this.log.debug("< getType result: {}", type);
				return type;
			} else {
				this.log.debug("< getType result: {}", ExpressionType.OBJECT);
				return ExpressionType.OBJECT;
			}

		default:
			this.log.debug("< getType result: {}", ExpressionType.OBJECT);
			return ExpressionType.OBJECT;
		}
	}

}
